package com.itheima.reggie.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itheima.reggie.common.R;
import com.itheima.reggie.dtos.DishDto;
import com.itheima.reggie.entity.Category;
import com.itheima.reggie.entity.Dish;
import com.itheima.reggie.entity.DishFlavor;
import com.itheima.reggie.ex.CustomerException;
import com.itheima.reggie.mapper.DishMapper;
import com.itheima.reggie.service.CategoryService;
import com.itheima.reggie.service.DishFlavorService;
import com.itheima.reggie.service.DishService;
import com.itheima.reggie.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author ghy
 * @version v1.0
 * create on 2022/11/18 15:35
 */
@Service
@Slf4j
@Transactional
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {

    @Autowired
    private DishFlavorService dishFlavorService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private DishMapper dishMapper;


    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 查询菜品包括口味数据
     * @param dish
     * @return
     */
    @Override
    public R findListAndFlavors(Dish dish) {

        log.warn("categoryId:{}", dish.getCategoryId());
        // 1.从缓存中获取数据
        // -- 缓存菜品的key应该如何设置？ dish_分类ID_起售状态
        // -- 以什么类型的数据来存储菜品数据？
        String key = RedisUtils.createKey("dish", dish.getCategoryId() + "_1");
        // -- 使用RedisTemplate在存储字符串数据时，可能直接存储集合数据，RedisTemplate会对当前数据做序列化操作
        List<DishDto> dishDtoList = (List<DishDto>) redisTemplate.opsForValue().get(key);
        //1.1 有，直接返回数据
        if(!CollectionUtils.isEmpty(dishDtoList)) {
            log.warn("从缓存中查询菜品--->1111");
            return R.success(dishDtoList);
        }

        //1.2 没有，则查询数据库，查询后放入缓存
        //1.2.1 查询数据库
        log.warn("从数据中查询菜品，并存入缓存--->2222");
        LambdaQueryWrapper<Dish> lqw = Wrappers.<Dish>lambdaQuery()
                .eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId())   //category_id = ?
                .eq(Dish::getStatus, 1)   //status = 1
                .orderByAsc(Dish::getSort)   // order by sort asc
                .orderByDesc(Dish::getUpdateTime);// order by update_time desc
        List<Dish> dishList = this.list(lqw);
        //封装口味数据
        dishDtoList = dishList.stream()
                .map(item->{
                    DishDto dishDto = new DishDto();
                    BeanUtils.copyProperties(item, dishDto);
                    //select * from dish_flavor where dish_id = ?
                    LambdaQueryWrapper<DishFlavor> wrapper = Wrappers.<DishFlavor>lambdaQuery().eq(DishFlavor::getDishId, item.getId());
                    List<DishFlavor> flavors = dishFlavorService.list(wrapper);
                    dishDto.setFlavors(flavors);
                    return dishDto;
                }).collect(Collectors.toList());

        //1.2.2 放入缓存，设置过期时间为1小时
        redisTemplate.opsForValue().set(key, dishDtoList, 1, TimeUnit.HOURS);
        return R.success(dishDtoList);
    }


    @Override
    public R updateWithFlavors(DishDto dishDto) {
        // 在更新前，查询菜品原来属于哪个分类   湘菜
        Long id = dishDto.getId();
        Dish dbDish = this.getById(id);

        //1.保存菜品基本数据
        this.updateById(dishDto);

        // 更新菜品数据成功后，删除对应分类的菜品。下次查询，缓存中没有就会查询数据库
        // a.修改最新的分类数据
        String key = RedisUtils.createKey("dish", dishDto.getCategoryId() + "_1");
        redisTemplate.delete(key);

        //由 湘菜-->川菜，就得修改缓存中两个分类的数据
        // b.判断是否修改了菜品的分类
        //dbDish.getCategoryId()    dish原来分类ID
        //dishDto.getCategoryId()   更新后的分类ID
        if(!dbDish.getCategoryId().equals(dishDto.getCategoryId())) {
            //修改了菜品的分类
            key = RedisUtils.createKey("dish", dbDish.getCategoryId() + "_1");
            redisTemplate.delete(key);
        }

        //2.删除原来菜品关联的口味数据
        dishFlavorService.remove(Wrappers.<DishFlavor>lambdaQuery().eq(DishFlavor::getDishId, dishDto.getId()));

        //2.保存菜品口味数据
        List<DishFlavor> flavors = dishDto.getFlavors();

        //为口味列表增加菜品ID
        flavors = flavors.stream().map(item -> {
            //为每个菜品品味设置菜品ID
            item.setDishId(dishDto.getId());
            return item;
        }).collect(Collectors.toList());

        dishFlavorService.saveBatch(flavors);
        return R.success("菜品修改成功");
    }

    /**
     * 根据ID查询菜品和口味数据
     * @param id
     * @return
     */
    @Override
    public R findByIdWithFlavors(Long id) {

        Dish dish = this.getById(id);
        LambdaQueryWrapper<DishFlavor> lqw = Wrappers.<DishFlavor>lambdaQuery().eq(DishFlavor::getDishId, id);
        List<DishFlavor> flavors = dishFlavorService.list(lqw);

        DishDto dishDto = new DishDto();
        BeanUtils.copyProperties(dish, dishDto);
        dishDto.setFlavors(flavors);
        return R.success(dishDto);
    }

    @Override
    public R findPage2(Integer page, Integer pageSize, String name) {
        if(page == null || pageSize == null) {
            throw new CustomerException("非法参数");
        }
        PageHelper.startPage(page, pageSize);
        if(StringUtils.isNotBlank(name)) {
            name = "%" + name.trim() + "%";
        }
        List<DishDto> dishDtos = dishMapper.findPage(page, pageSize,name);
        PageInfo<DishDto> pageInfo = new PageInfo<>(dishDtos);
        IPage<DishDto> dishDtoIPage = new Page<>(page, pageSize);
        dishDtoIPage.setRecords(pageInfo.getList());
        dishDtoIPage.setSize(pageInfo.getSize());
        dishDtoIPage.setTotal(pageInfo.getTotal());
        return R.success(dishDtoIPage);
    }

    @Override
    public R findPage(Integer page, Integer pageSize, String name) {

        if(page == null || pageSize == null) {
            throw new CustomerException("非法参数");
        }

        //1.查询菜品基本数据
        IPage<Dish> dishIPage = new Page<>(page, pageSize);
        IPage<DishDto> dishDtoIPage = new Page<>(page, pageSize);

        LambdaQueryWrapper<Dish> lqw = Wrappers.<Dish>lambdaQuery()
                .like(StringUtils.isNotBlank(name), Dish::getName, name)
                .orderByDesc(Dish::getUpdateTime)
                .orderByAsc(Dish::getSort);

        this.page(dishIPage, lqw);

        //属性的拷贝：前提属性名必须要一致
        //参数一：被拷贝的对象
        //参数二：接收数据的目标对象
        //参数三：哪些属性不要拷贝
        BeanUtils.copyProperties(dishIPage, dishDtoIPage, "records");

        //2.封装菜品分类数据
        List<Dish> dishes = dishIPage.getRecords();
        List<DishDto> records = dishes.stream().map(item -> {
            DishDto dishDto = new DishDto();
            //属性拷贝
            BeanUtils.copyProperties(item,dishDto);
            //根据分类ID查询分类数据
            Category category = categoryService.getById(item.getCategoryId());
            if(null != category) {
                //为DishDto设置分类名称
                dishDto.setCategoryName(category.getName());
            }
            return dishDto;
        }).collect(Collectors.toList());
        dishDtoIPage.setRecords(records);

        return R.success(dishDtoIPage);
    }

    @Override
    public R add(DishDto dishDto) {

        //1.保存菜品基本数据
        this.save(dishDto);

        //保存菜品数据成功后，删除对应分类的菜品。下次查询，缓存中没有就会查询数据库
        String key = RedisUtils.createKey("dish", dishDto.getCategoryId() + "_1");
        redisTemplate.delete(key);

        //2.保存菜品口味数据
        List<DishFlavor> flavors = dishDto.getFlavors();

        //为口味列表增加菜品ID
        flavors = flavors.stream().map(item -> {
            //为每个菜品品味设置菜品ID
            item.setDishId(dishDto.getId());
            return item;
        }).collect(Collectors.toList());

        dishFlavorService.saveBatch(flavors);
        return R.success("菜品保存成功");
    }

}
